package model.strategies.metododacruz;

import model.cubos.Adesivo;
import model.cubos.Cor;
import model.cubos.CuboMagico;
import model.cubos.CuboMagicoImpl;
import model.rotations.RotacaoCamadaDireita;
import model.rotations.RotacaoCamadaEsquerda;
import model.rotations.RotacaoCamadaInferior;
import model.rotations.RotacaoCamadaSuperior;
import model.rotations.RotacaoCuboInteiroComBaseNaCamadaEntreSuperiorInferior;
import model.rotations.RotacaoCuboInteiroComBaseNaCamadaSuperior;
import model.rotations.RotacaoFatoryException;
import model.strategies.MyStrategiesException;

/**
 * Classe que implementa o algoritmo que resolve a terceira etapa do algoritmo
 * básico do método da cruz: Fechar a camada do meio das faces laterais com
 * adesivos da mesma cor do centro.
 * 
 * @author elton
 */
public class AlgoritmoBasicoEtapaTres extends AlgoritmoBasico {

	@Override
	public void resolver(CuboMagico cubo) throws MyStrategiesException {
		try {
			setCubo((CuboMagicoImpl) cubo);
			resolverCamadaDoMeio();
		} catch (Exception e) {
			throw new MyStrategiesException(
					"Erro ao executar a terceira etapa do algoritmo basico de resolução do cubo mágico",
					e);
		}
	}

	/**
	 * Método que retorna os movimentos necessários para resolução da etapa
	 * Camada do Meio do Cubo a partir de uma configuração recebida juntamente
	 * com a explicação necessária
	 * 
	 * @throws RotacaoFatoryException
	 */
	public void resolverCamadaDoMeio() throws RotacaoFatoryException {

		boolean isNotCamadaDoMeioResolvida = true;
		int numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior = 0;
		Adesivo[][][] matriz = cubo.getCubo();

		while (isNotCamadaDoMeioResolvida) {
			isNotCamadaDoMeioResolvida = isCamadaDoMeioResolvida(matriz);
			numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior = 0;
			if (isNotCamadaDoMeioResolvida) {
				numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior = obterNumeroDeAdesivosAmareloNosMeiosDaCamadaDeCima(
						numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior,
						matriz);
			}
			if ((numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior < 4)
					&& (isNotCamadaDoMeioResolvida)) {
				resolverCamadaDoMeio(matriz);
			} else {
				if ((numeroDeAdesivosAmarelosNosMeiosDaCamadaSuperior == 4)
						&& (isNotCamadaDoMeioResolvida)) {
					retirarAmarelosDoMeioDaCamadaSuperior(matriz);
				}
			}
		}
		return;
	}

	private void retirarAmarelosDoMeioDaCamadaSuperior(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		int k;
		int flag3;
		flag3 = 0;
		if (matriz[0][1][1].isCor(Cor.AZUL)/*
											 * matriz[0][1][1].getKey () == 4
											 */) { // verificando
													// pecas erradas
													// na face azul
			k = 3;
			setOrigem(matriz[0][1][1]);

			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {

					if (matriz[i][1][0].getKey() != k && i == 0)
						flag3 = 1;
					else if (matriz[i][1][2].getKey() != k + 2 && i == 0)
						flag3 = 0;
					else if (matriz[i][1][0].getKey() != k && i == 2) {
						flag3 = 1;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					} else if (matriz[i][1][2].getKey() != k + 2 && i == 2) {
						flag3 = 0;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					}
				}
				k = k + 18;
			}
		}

		if (matriz[0][1][1].isCor(Cor.VERMELHO)/*
												 * matriz[0][1][1]. getKey() ==
												 * 13
												 */) { // verificando
														// pecas
														// erradas
														// na face
														// vermelha
			k = 12;
			setOrigem(matriz[0][1][1]);

			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					if (matriz[i][1][0].getKey() != k && i == 0)
						flag3 = 1;
					else if (matriz[i][1][2].getKey() != k + 2 && i == 0)
						flag3 = 0;
					else if (matriz[i][1][0].getKey() != k && i == 2) {
						flag3 = 1;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					} else if (matriz[i][1][2].getKey() != k + 2 && i == 2) {
						flag3 = 0;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					}
				}
				k = k + 18;
			}
		}

		if (matriz[0][1][1].isCor(Cor.VERDE)/*
											 * matriz[0][1][1].getKey () == 22
											 */) { // verificando
													// pecas erradas
													// na face verde
			k = 21;
			setOrigem(matriz[0][1][1]);
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					if (matriz[i][1][0].getKey() != k && i == 0)
						flag3 = 1;
					else if (matriz[i][1][2].getKey() != k + 2 && i == 0)
						flag3 = 0;
					else if (matriz[i][1][0].getKey() != k && i == 2) {
						flag3 = 1;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					} else if (matriz[i][1][2].getKey() != k + 2 && i == 2) {
						flag3 = 0;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					}
				}
				k = k - 18;
			}
		}

		if (matriz[0][1][1].getKey() == 31) { // verificando pecas
												// erradas na face
												// laranja
			k = 30;
			setOrigem(matriz[0][1][1]);
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					if (matriz[i][1][0].getKey() != k && i == 0)
						flag3 = 1;
					else if (matriz[i][1][2].getKey() != k + 2 && i == 0)
						flag3 = 0;
					else if (matriz[i][1][0].getKey() != k && i == 2) {
						flag3 = 1;
						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					} else if (matriz[i][1][2].getKey() != k + 2 && i == 2) {
						flag3 = 0;

						setOrigem(matriz[i][1][0]);

						aplicarRotacaoNoSentidoHorario(
								RotacaoCuboInteiroComBaseNaCamadaSuperior.class,
								2);
					}
				}
				k = k - 18;
			}
		}

		if (flag3 == 1) {
			aplicarAlgoritmoUm();
		} else if (flag3 == 0) {
			aplicarAlgoritmoDois();
		}
	}

	private void aplicarAlgoritmoDois() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaEsquerda.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaEsquerda.class);
	}

	private void aplicarAlgoritmoUm() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaEsquerda.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaEsquerda.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCuboInteiroComBaseNaCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaDireita.class);
	}

	private void resolverCamadaDoMeio(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		int flag2;
		flag2 = 0;

		setDestino(matriz[0][0][1]);

		while (matriz[0][0][1].isCor(Cor.AMARELO) || /*
													 * enquanto for amarelo
													 *//*
														 * matriz[0][0][1
														 * ].getKey() >= 36 &&
														 * matriz[0 ][0][1] <=44
														 * || matriz[4][2
														 * ][1].getKey() >= 36
														 * && matriz [4][2][1]
														 * <=44
														 */
		matriz[4][2][1].isCor(Cor.AMARELO)) {
			aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		}

		setDestino(matriz[0][0][1]);

		if ((/* matriz[0][0][1].isCor(Cor.AZUL) */matriz[0][0][1].getKey() == 3)
				|| (matriz[0][0][1].getKey() == 5)) {// se
														// for
														// azul

			if (!(matriz[0][1][1].isCor(Cor.AZUL)/*
												 * matriz[0][1][1].getKey () ==
												 * 4
												 */)) {
				setDestino(matriz[0][1][1]);

				while (!matriz[0][1][1].isCor(Cor.AZUL)/*
														 * matriz[0][1][1
														 * ].getKey() != 4
														 */) {// enquanto
					aplicarAlgoritmoTres();
				}

			}
			if (matriz[0][0][1].getKey() == 3)
				flag2 = 0;
			else if (matriz[0][0][1].getKey() == 5)
				flag2 = 1;
		} else {
			if (/* matriz[0][0][1].isCor(Cor.VERMELHO) */(matriz[0][0][1]
					.getKey() == 12) || (matriz[0][0][1].getKey() == 14)) {// se
																			// for
																			// vermelho
				if (!(/* matriz[0][1][1].isCor(Cor.VERMELHO) */matriz[0][1][1]
						.getKey() == 13)) {
					setDestino(matriz[0][1][1]);
					while (/* !matriz[0][1][1].isCor(Cor.VERDE) */matriz[0][1][1]
							.getKey() != 13) {
						aplicarAlgoritmoTres();
					}
				}

				if (matriz[0][0][1].getKey() == 12)
					flag2 = 0;
				else if (matriz[0][0][1].getKey() == 14)
					flag2 = 1;
			} else {
				if (/* matriz[0][0][1].isCor(Cor.VERDE) */(matriz[0][0][1]
						.getKey() == 21) || (matriz[0][0][1].getKey() == 23)) {
					if (!(/* matriz[0][1][1].isCor(Cor.VERDE) */matriz[0][1][1]
							.getKey() == 22)) {
						setDestino(matriz[0][1][1]);
						while (/* !matriz[0][1][1].isCor(Cor.VERDE) */matriz[0][1][1]
								.getKey() != 22) {
							aplicarAlgoritmoTres();
						}
					}
					if (matriz[0][0][1].getKey() == 21)
						flag2 = 0;
					else if (matriz[0][0][1].getKey() == 23)
						flag2 = 1;
				} else {
					if (/* matriz[0][0][1].isCor(Cor.LARANJA) */(matriz[0][0][1]
							.getKey() == 30)
							|| (matriz[0][0][1].getKey() == 32)) {
						if (!(matriz[0][1][1].isCor(Cor.LARANJA)/*
																 * matriz[ 0
																 * ][1][ 1
																 * ].getKey ()
																 * == 31
																 */)) {
							setDestino(matriz[0][1][1]);
							while (/*
									 * !matriz[0][1][1].isCor(Cor.LARANJA )
									 */matriz[0][1][1].getKey() != 31) {
								aplicarAlgoritmoTres();
							}
						}
						if (matriz[0][0][1].getKey() == 30)
							flag2 = 0;
						else if (matriz[0][0][1].getKey() == 32)
							flag2 = 1;
					}
				}
			}
		}
		setDestino(matriz[4][2][1]);

		if (flag2 == 0) {
			setDestino(matriz[1][1][1]);
			aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
			aplicarAlgoritmoUm();
		} else {
			if (flag2 == 1) {
				setDestino(matriz[3][1][1]);
				aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
				aplicarAlgoritmoDois();
			}
		}
	}

	private void aplicarAlgoritmoTres() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaInferior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaEntreSuperiorInferior.class);
	}

	private int obterNumeroDeAdesivosAmareloNosMeiosDaCamadaDeCima(int cont,
			Adesivo[][][] matriz) {
		for (int j = 0; j < 4; j++) {
			if (matriz[j][0][1].isCor(Cor.AMARELO)/*
												 * matriz[j][0][1].getKey () >=
												 * 36 && matriz[j][0][1] <=44
												 */)
				cont++;
		}
		if (matriz[4][0][1].isCor(Cor.AMARELO)/*
											 * matriz[4][0][1].getKey() >= 36 &&
											 * matriz[4][0][1] <=44
											 */)
			cont++;
		if (matriz[4][1][0].isCor(Cor.AMARELO)/*
											 * matriz[4][1][0].getKey() >= 36 &&
											 * matriz[4][1][0] <=44
											 */)
			cont++;
		if (matriz[4][1][2].isCor(Cor.AMARELO)/*
											 * matriz[4][1][2].getKey() >= 36 &&
											 * matriz[4][1][2] <=44
											 */)
			cont++;
		if (matriz[4][2][1].isCor(Cor.AMARELO)/*
											 * matriz[4][2][1].getKey() >= 36 &&
											 * matriz[4][2][1] <=44
											 */)
			cont++;
		return cont;
	}

	private boolean isCamadaDoMeioResolvida(Adesivo[][][] matriz) {
		boolean isNotCamadaDoMeioResolvida;
		isNotCamadaDoMeioResolvida = false;

		isNotCamadaDoMeioResolvida = isCamadaDoMeioAzulResolvida(
				isNotCamadaDoMeioResolvida, matriz);

		isNotCamadaDoMeioResolvida = isCamadaDoMeioVermelhaResolvida(
				isNotCamadaDoMeioResolvida, matriz);

		isNotCamadaDoMeioResolvida = isCamadaDoMeioVerdeResolvida(
				isNotCamadaDoMeioResolvida, matriz);

		isNotCamadaDoMeioResolvida = isCamadaDoMeioLaranjaResolvida(
				isNotCamadaDoMeioResolvida, matriz);
		return isNotCamadaDoMeioResolvida;
	}

	private boolean isCamadaDoMeioLaranjaResolvida(
			boolean isNotCamadaDoMeioResolvida, Adesivo[][][] matriz) {
		int k;
		if (matriz[0][1][1].getKey() == 31) { // teste quando a face LARANJA
												// está para frente
			k = 30;
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					isNotCamadaDoMeioResolvida = true;
				}
				k = k - 18;
			}
		}
		return isNotCamadaDoMeioResolvida;
	}

	private boolean isCamadaDoMeioVerdeResolvida(
			boolean isNotCamadaDoMeioResolvida, Adesivo[][][] matriz) {
		int k;
		if (matriz[0][1][1].getKey() == 22) { // teste quando a face VERDE
												// está para frente
			k = 21;
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					isNotCamadaDoMeioResolvida = true;
				}
				k = k - 18;
			}
		}
		return isNotCamadaDoMeioResolvida;
	}

	private boolean isCamadaDoMeioVermelhaResolvida(
			boolean isNotCamadaDoMeioResolvida, Adesivo[][][] matriz) {
		int k;
		if (matriz[0][1][1].getKey() == 13) { // teste quando a face
												// VERMELHA está para
												// frente
			k = 12;
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					isNotCamadaDoMeioResolvida = true;
				}
				k = k + 18;
			}
		}
		return isNotCamadaDoMeioResolvida;
	}

	private boolean isCamadaDoMeioAzulResolvida(
			boolean isNotCamadaDoMeioResolvida, Adesivo[][][] matriz) {
		int k;
		if (/* matriz[0][1][1].isCor(Cor.AZUL) */matriz[0][1][1].getKey() == 4) { // teste
																					// quando
																					// a
																					// face
																					// AZUL
																					// está
																					// para
																					// frente
			k = 3;
			for (int i = 0; i < 4; i = i + 2) {
				if ((matriz[i][1][0].getKey() != k)
						|| (matriz[i][1][2].getKey() != k + 2)) {
					isNotCamadaDoMeioResolvida = true;
				}
				k = k + 18;
			}
		}
		return isNotCamadaDoMeioResolvida;
	}

}
